//VolumetricFog
//Written by MJ_Ehsan for Reshade
//Version 1.1a

//license
//CC0 ^_^

///////////////Include/////////////////////

#include "ReShade.fxh"

#define pix float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT);

#ifndef MAX_MIPSLEVELS_
 #define MAX_MIPSLEVELS_ 2
#endif

#if MAX_MIPSLEVELS_ > 10
 #undef MAX_MIPSLEVELS_
 #define MAX_MIPSLEVELS_ 10
#endif

uniform float Timer < source = "timer"; >;

///////////////Include/////////////////////
///////////////Textures-Samplers///////////

texture2D TexColor : COLOR;
sampler  sTexColor {Texture = TexColor; };

texture2D FogTex { Width = BUFFER_WIDTH;   Height = BUFFER_HEIGHT;   Format = RGBA8;};
sampler2D sFogTex { Texture = FogTex;};

texture2D BlurTex 	{ Width = BUFFER_WIDTH;   Height = BUFFER_HEIGHT;   Format = RGBA8; MipLevels = MAX_MIPSLEVELS_+1; };
sampler2D sBlurTex	{ Texture = BlurTex;	};

///////////////Textures-Samplers///////////
///////////////UI//////////////////////////

uniform float radius <
	ui_type = "slider";
	ui_max = 200;
	ui_label = "Blur Radius";
	ui_tooltip = "Increase it along with Intensity\n"
				 "if you want a denser fog."; 
	ui_category = "Blurring";
> = 100;

uniform int SampleCount <
	ui_label = "Blur Quality";
	ui_tooltip = "Increase it to reduce noise.\n"
				 "Also look at Temporal Accumulation or Denoiser\n"
				 "Cuz this option is a performance killer";
	ui_max = 8;
	ui_min = 1;
	ui_type = "slider";
	ui_category = "Blurring";
> = 2;

uniform bool TemporalAccum <
	ui_type = "radio";
	ui_label = "Temporal Accumulation";
	ui_tooltip = "If you are using (TFAA) from jak0b,\n"
				 "use this option to improve blur quality for free";
	ui_category = "Blurring";
> = 1;

uniform float Intensity <
	ui_max = 8;
	ui_min = 0;
	ui_type = "slider";
	ui_tooltip = "Overall power of fog.";
	ui_category = "Blending";
> = 1;

uniform float MaxIntensity <
	ui_type = "slider";
	ui_label = "Maximum Intensity";
	ui_max = 1;
	ui_tooltip = "Intensity of fog is relative to depth\n"
				 "Objects further away will be affected more than near ones\n"
				 "You can use this option to avoid fading distant objects completely.";
	ui_category = "Blending";
> = 0.8;

uniform float Exposure <
	ui_type = "slider";
	ui_max = 2;
	ui_category = "Blending";
> = 1;

uniform float Gamma <
	ui_type = "slider";
	ui_max = 2;
	ui_category = "Blending";
> = 1;

//uniform bool Shadow <> = 0;
//static const bool Shadow = 0;


///////////////UI//////////////////////////
///////////////Functions///////////////////

float noise(float2 co)
{
	return frac(sin(dot(co.xy ,float2(1.0,73))) * 43758.5453);
}

float3 noise3dts(float2 co, int s, bool t)
{
	co += sin(Timer/64)*t;
	co += frac(s/3.1415926535);
	return float3( noise(co), noise(co+0.6432168421), noise(co+0.19216811));
}

///////////////Functions///////////////////
///////////////Vertex Shader///////////////
///////////////Vertex Shader///////////////
///////////////Pixel Shader////////////////

float3 Fog( float4 Postion : SV_Position, float2 texcoord : TEXCOORD0) : SV_Target
{
	float depth = ReShade::GetLinearizedDepth(texcoord);
	float2 p = pix;
	
	float4 color = float4(tex2D(sTexColor, texcoord).rgb,1);
	float4 S = color;
	float Radius = sqrt(depth)*radius;
	uint iteration = Radius*SampleCount/8;
	
	for (int i = 1; i <= iteration; i++)
	{	
		float2 seed = noise3dts( texcoord.xy, i, TemporalAccum).rg;	
		//float distance = (i + seed.x)/iteration;
		float distance = seed.x;
		//float ang = frac(seed.x + i * 0.6180339887498) * 3.1415927 * 2.0;
		float ang = ((i + seed.y) / iteration) * 3.14159265 * 2;
		
		float2 offset; sincos(ang, offset.y, offset.x); 
		offset *= sqrt(distance);
		offset *= p.xy * Radius;
		
		float Jdepth =  ReShade::GetLinearizedDepth(texcoord + offset).r;
		if( Jdepth >= depth -0.01)
		{
			S += float4( tex2Dlod( sTexColor, float4(texcoord + offset,0,0)).rgb, 1);
		}
	}

	S.rgb /= S.a;
	
	float coeff = min( depth*Intensity, MaxIntensity);
	//if(Shadow) S = lerp(S, S * saturate(tex2D(sCommonTex0, texcoord)), min((1-depth)*Intensity, 1));
	
	return S.rgb;
}

float3 Blur( float4 Postion : SV_Position, float2 texcoord : TEXCOORD0) : SV_Target
{
	float depth = ReShade::GetLinearizedDepth(texcoord);
	float2 p = pix;
	
	float4 color = float4(tex2D(sFogTex, texcoord).rgb,1);
	float4 S = color;

	float Radius = radius/16;
	uint iteration = max(SampleCount, 2); //iteration = 0;
	
	for (int i = 1; i <= iteration; i++)
	{	
		float2 seed = noise3dts( texcoord.xy + 0.33463545721, i, TemporalAccum).rg;
		
		float distance = seed.x;
		float ang = ((i + seed.y) / iteration) * 3.14159265 * 2;
		
		float2 offset; sincos(ang, offset.y, offset.x); 
		offset *= sqrt(distance);
		offset *= p.xy * Radius;
		
		float Jdepth =  ReShade::GetLinearizedDepth(texcoord + offset).r;
		if( Jdepth >= depth -0.01)
		{
			S += float4( tex2Dlod( sFogTex, float4(texcoord + offset,0,0)).rgb, 1);
		}
	}

	S.rgb /= S.a;
	S = pow( abs(S), Gamma)*Exposure;
	return S.rgb;
}

float3 Blend( float4 Postion : SV_Position, float2 texcoord : TEXCOORD0) : SV_Target
{
	float depth = ReShade::GetLinearizedDepth(texcoord);
	float2 p = pix; p *= pow(2, MAX_MIPSLEVELS_);
	
	float4 fog; fog.a = 1; fog.rgb = tex2Dlod(sBlurTex, float4(texcoord,0,MAX_MIPSLEVELS_)).rgb;
	
	float2 offset[8] =
	{
		float2(  0, p.y), float2( p.x,  0), float2(   0, -p.y), float2(-p.x,    0),
		float2(p.x, p.y), float2(-p.x,p.y), float2(-p.x, -p.y), float2( p.x, -p.y)
	};
	
	
	if( MAX_MIPSLEVELS_ != 0)
	{
		float4 FogSample; FogSample.a = 1;
		float3 DepthSample;
		for(int i; i<8; i++)
		{
			FogSample.rgb = tex2Dlod(sBlurTex, float4(texcoord + offset[i]/2, 0, MAX_MIPSLEVELS_)).rgb;
			DepthSample = ReShade::GetLinearizedDepth(texcoord + offset[i]*1.5);
			if( abs( DepthSample.x - depth.x) < 0.01) fog += FogSample;
		}
		fog /= fog.a;
	}
	
	float3 back= tex2D(sTexColor, texcoord).rgb;
	
	float coeff = min( depth * Intensity, MaxIntensity);
	
	return lerp( back, fog.rgb, coeff);
}

///////////////Pixel Shader////////////////
///////////////Techniques//////////////////

technique VolumetricFog <
	ui_label = "Volumetric Fog";
	ui_tooltip = "Screen  Space  Indirect  Volumetric  Lighting.\n"
				 "              ||By Ehsan2077||                \n"
				 "Recommended to use with TFAA for more quality."; 
>
{
	pass
	{
		VertexShader = PostProcessVS;
		PixelShader = Fog;
		RenderTarget = FogTex;
	}
	pass
	{
		VertexShader = PostProcessVS;
		PixelShader = Blur;
		RenderTarget = BlurTex;
	}
	pass
	{		
		VertexShader = PostProcessVS;
		PixelShader = Blend;
	}
}

///////////////Techniques//////////////////
